package org.metasketch.simulator.runtime;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.metasketch.simulator.resources.Resource;
import org.metasketch.simulator.resources.ResourceBank;
import org.metasketch.simulator.resources.ResourceBase;
import org.metasketch.simulator.resources.ResourceClass;

public class ResourcePool {

	Map<ResourceBase, Long> resources = new HashMap<ResourceBase, Long>();

	public void initialize(ResourceBank resourceBank) {
		resourceBank.getResourceClasses();
		List<Resource> rs = resourceBank.getResources();
		for (Resource resource : rs) {
			Long quantity = resource.getQuantity();
			addResource(resource, quantity);
		}
	}

	public boolean canRequest(ResourceBase resource, Long amount)
			throws Exception {
		if (resource instanceof Resource)
			return canRequestResource((Resource) resource, amount);
		else if (resource instanceof ResourceClass)
			return canRequestResourceClass((ResourceClass) resource, amount);

		throw new Exception("Not a recognized ResourceBase subclass....");

	}

	public Map<Resource, Long> request(ResourceBase resource, Long amount)
			throws Exception {

		if (resource instanceof Resource) {
			requestResource((Resource) resource, amount);

			Map<Resource, Long> assigned = new HashMap<Resource, Long>();
			assigned.put((Resource) resource, amount);
			return assigned;
		} else if (resource instanceof ResourceClass) {

			return requestResourceClass((ResourceClass) resource, amount);

		}

		throw new Exception("Not a recognized ResourceBase subclass....");

	}

	public void release(ResourceBase resource, Long amount) throws Exception {
		if (resource instanceof Resource)
			releaseResource((Resource) resource, amount);
		// else if (resource instanceof ResourceClass)
		// releaseResourceClass((ResourceClass) resource, amount);
		else
			throw new Exception(
					"Can only release Resources , not ResourceClasses or otherwise...");

	}

	public void release(Map<ResourceBase, Long> resources2) throws Exception {
		Set<Entry<ResourceBase, Long>> entries = resources2.entrySet();
		for (Entry<ResourceBase, Long> entry : entries) {
			release(entry.getKey(), entry.getValue());
		}

	}

	public void releaseResources(Map<Resource, Long> resources2)
			throws Exception {
		Set<Entry<Resource, Long>> entries = resources2.entrySet();
		for (Entry<Resource, Long> entry : entries) {
			release(entry.getKey(), entry.getValue());
		}

	}

	public boolean canRequestResource(Resource resource, Long quantity) {
		Long available = resources.get(resource);

		if (available == null)
			return false;

		if (available >= quantity)
			return true;

		return false;
	}

	public void requestResource(Resource resource, Long quantity) {
		Long available = resources.get(resource);
		available -= quantity;
		resources.put(resource, available);

	}

	public void releaseResource(Resource resource, Long quantity) {
		Long available = resources.get(resource);
		if (available == null)
			available = 0l;

		available += quantity;
		resources.put(resource, available);

		Long qty = resources.get(resource);
		qty.intValue();
		;

	}

	public boolean canRequestResourceClass(ResourceClass resourceClass,
			Long quantity) {
		List<ResourceBase> classResources = resourceClass.getResources();
		// TODO this needs a recursive method to go through nested classes until
		// actual resources
		Long totalQuantity = quantity;
		for (ResourceBase resourceBase : classResources) {
			Long available = resources.get(resourceBase);

			if (available == null)
				return false;

			if (available <= totalQuantity) {
				totalQuantity -= available;
			} else
				// can just return true...
				totalQuantity -= quantity;

			if (totalQuantity == 0)
				return true;
		}
		return false;
	}

	public Map<Resource, Long> requestResourceClass(
			ResourceClass resourceClass, Long quantity) {
		Map<Resource, Long> assigned = new HashMap<Resource, Long>();
		List<ResourceBase> classResources = resourceClass.getResources();
		// TODO this needs a recursive method to go through nested classes until
		// actual resources
		Long totalQuantity = quantity;
		for (ResourceBase resourceBase : classResources) {
			Long available = resources.get(resourceBase);
			if (available <= totalQuantity) {

				requestResource((Resource) resourceBase, available);
				totalQuantity -= available;

				Long qty = assigned.get(resourceBase);
				if (qty != null)
					qty += available;
				else
					qty = available;

				assigned.put((Resource) resourceBase, qty);

			} else {

				requestResource((Resource) resourceBase, totalQuantity);

				Long qty = assigned.get(resourceBase);
				if (qty != null)
					qty += totalQuantity;
				else
					qty = totalQuantity;

				assigned.put((Resource) resourceBase, totalQuantity);

				totalQuantity = 0l;

			}

		}

		return assigned;

	}

	// public void addResources(Map<Resource, Long> requested) {
	// Set<Entry<ResourceBase, Long>> entries = resources2.entrySet();
	// for (Entry<ResourceBase, Long> entry : entries) {
	// release(entry.getKey(), entry.getValue());
	// }
	//
	// }
	//
	// private void addRequested(Map<Resource, Long> assigned,
	// Map<Resource, Long> requested) {
	//
	// Set<Entry<Resource, Long>> entries = requested.entrySet();
	// for (Entry<Resource, Long> entry : entries) {
	// Resource resource = entry.getKey();
	// Long amount = entry.getValue();
	//
	// Long qty = assigned.get(resource);
	// if (qty != null)
	// qty += amount;
	// else
	// qty = amount;
	//
	// assigned.put(resource, qty);
	//
	// }
	//
	// }

	public void addResource(ResourceBase resource, Long amount) {
		resources.put(resource, amount);
	}

	public Map<ResourceBase, Long> getResources() {
		return resources;
	}

	public void setResources(Map<ResourceBase, Long> resources) {
		this.resources = resources;
	}

}
